--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
scripts/unify-backend-plain-files.sh 65ca9ddc823fcc2c8f8653c553b944b85db0875f (65ca9ddc) Text, 8.24 KB
T8b949e#!/usr/bin/env bash
T8b949e# Make the two per-arch cx_Freeze backend trees compatible with
T8b949e# @electron/universal's merge requirements:
T8b949e#
T8b949e# 1. Every file must exist in BOTH trees (no unique-to-one-arch files).
T8b949e# 2. Every non-Mach-O file must be byte-identical across trees.
T8b949e#
T8b949e# Python bytecode (.pyc inside library.zip) is architecture-independent.
T8b949e# Only timestamps and zip metadata cause SHA differences.
Tffa657set -euo pipefail
Te6edf3ROOTTff7b72=Ta5d6ff"Tff7b72$(Tffa657cd Ta5d6ff"Tff7b72$(dirname Ta5d6ff"Te6edf3$0Ta5d6ff"Tff7b72)Ta5d6ff/..Ta5d6ff" Tff7b72&& Tffa657pwdTff7b72)Ta5d6ff"
Te6edf3ARM64_DIRTff7b72=Ta5d6ff"Te6edf3$ROOTTa5d6ff/build/exe/darwin-arm64Ta5d6ff"
Te6edf3X64_DIRTff7b72=Ta5d6ff"Te6edf3$ROOTTa5d6ff/build/exe/darwin-x64Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ ! -d Ta5d6ff"Te6edf3$ARM64_DIRTa5d6ff" Tff7b72|| ! -d Ta5d6ff"Te6edf3$X64_DIRTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"unify-backend: one or both backend dirs missing, skipping"
Tffa657exit T79c0ff0
Tff7b72fi
Te6edf3unifiedTff7b72=T79c0ff0
Te6edf3syncedTff7b72=T79c0ff0
Te6edf3droppedTff7b72=T79c0ff0
Te6edf3normalizedTff7b72=T79c0ff0
copy_missingTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3src_dirTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff" Te6edf3dst_dirTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff" Te6edf3labelTff7b72=Ta5d6ff"Te6edf3$3Ta5d6ff"
Tff7b72while Te6edf3IFSTff7b72= Tffa657read -r -d Ta5d6ff'' relTb4b4b4; Tff7b72do
Te6edf3relTff7b72=Ta5d6ff"Tffd700${Te6edf3relTb4b4b4#./Tffd700}Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ ! -f Ta5d6ff"Te6edf3$dst_dirTa5d6ff/Te6edf3$relTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657local Te6edf3src_fileTff7b72=Ta5d6ff"Te6edf3$src_dirTa5d6ff/Te6edf3$relTa5d6ff"
Tffa657local ft
Te6edf3ftTff7b72=Tff7b72$(file --brief --no-pad Ta5d6ff"Te6edf3$src_fileTa5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657trueTff7b72)
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$ftTa5d6ff" Tff7b72=Tff7b72= Mach-O* Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffunify-backend: dropping arch-only Mach-O for consistency: Te6edf3$relTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff"Ta5d6ff (Te6edf3$labelTa5d6ff); source reports: Te6edf3$ftTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" Hint: this native library/extension only exists in one arch's" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" cx_Freeze output. Check the corresponding per-OS dependency" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" install script builds/bundles it for both darwin-arm64 and" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" darwin-x64 (e.g. missing a Homebrew lib on one arch, or a" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" package that only ships a wheel for one arch)." >Tb4b4b4&T79c0ff2
rm -f Ta5d6ff"Te6edf3$src_fileTa5d6ff"
Te6edf3droppedTff7b72=Tff7b72$((Te6edf3dropped Tff7b72+ T79c0ff1Tff7b72))
Tff7b72continue
Tff7b72fi
mkdir -p Ta5d6ff"Te6edf3$dst_dirTa5d6ff/Tff7b72$(dirname Ta5d6ff"Te6edf3$relTa5d6ff"Tff7b72)Ta5d6ff"
cp Ta5d6ff"Te6edf3$src_fileTa5d6ff" Ta5d6ff"Te6edf3$dst_dirTa5d6ff/Te6edf3$relTa5d6ff"
Tffa657echo Ta5d6ff"Ta5d6ff synced (Te6edf3$labelTa5d6ff): Te6edf3$relTa5d6ff"
Te6edf3syncedTff7b72=Tff7b72$((Te6edf3synced Tff7b72+ T79c0ff1Tff7b72))
Tff7b72fi
Tff7b72done < <Tff7b72(Tffa657cd Ta5d6ff"Te6edf3$src_dirTa5d6ff" Tff7b72&& find . -type f -print0Tff7b72)
Tff7b72}
copy_missing Ta5d6ff"Te6edf3$ARM64_DIRTa5d6ff" Ta5d6ff"Te6edf3$X64_DIRTa5d6ff" Ta5d6ff"arm64 -> x64"
copy_missing Ta5d6ff"Te6edf3$X64_DIRTa5d6ff" Ta5d6ff"Te6edf3$ARM64_DIRTa5d6ff" Ta5d6ff"x64 -> arm64"
Tff7b72while Te6edf3IFSTff7b72= Tffa657read -r -d Ta5d6ff'' relTb4b4b4; Tff7b72do
Te6edf3relTff7b72=Ta5d6ff"Tffd700${Te6edf3relTb4b4b4#./Tffd700}Ta5d6ff"
Te6edf3arm64_fileTff7b72=Ta5d6ff"Te6edf3$ARM64_DIRTa5d6ff/Te6edf3$relTa5d6ff"
Te6edf3x64_fileTff7b72=Ta5d6ff"Te6edf3$X64_DIRTa5d6ff/Te6edf3$relTa5d6ff"
Tff7b72[Tff7b72[ -f Ta5d6ff"Te6edf3$x64_fileTa5d6ff" Tff7b72]Tff7b72] Tff7b72|| Tff7b72continue
Tff7b72if cmp -s Ta5d6ff"Te6edf3$arm64_fileTa5d6ff" Ta5d6ff"Te6edf3$x64_fileTa5d6ff"Tb4b4b4; Tff7b72then
Tff7b72continue
Tff7b72fi
Te6edf3filetypeTff7b72=Tff7b72$(file --brief --no-pad Ta5d6ff"Te6edf3$arm64_fileTa5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657trueTff7b72)
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$filetypeTa5d6ff" !Tff7b72= Mach-O* Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
T8b949e# .pyc is always portable CPython bytecode regardless of what else
T8b949e# lives in the same package directory: differences here come from
T8b949e# the embedded source mtime/size (PEP 552 header) or from hash-seed-
T8b949e# dependent constant ordering (e.g. frozenset literals), never from
T8b949e# the target architecture. Unlike a real native-extension conflict,
T8b949e# unifying it can't break the .so/.dylib siblings, which are handled
T8b949e# entirely separately below.
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$relTa5d6ff" Tff7b72=Tff7b72= *.pyc Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
cp Ta5d6ff"Te6edf3$arm64_fileTa5d6ff" Ta5d6ff"Te6edf3$x64_fileTa5d6ff"
Tffa657echo Ta5d6ff"Ta5d6ff unified (.pyc, source-independent of arch): Te6edf3$relTa5d6ff"
Te6edf3unifiedTff7b72=Tff7b72$((Te6edf3unified Tff7b72+ T79c0ff1Tff7b72))
Tff7b72continue
Tff7b72fi
T8b949e# cx_Freeze bundles pure-Python modules into lib/library.zip. Native
T8b949e# extensions are always written to the filesystem separately (they
T8b949e# can't be dlopen'd from inside a zip), so this archive's *contents*
T8b949e# are pure CPython bytecode just like loose .pyc files. Its raw bytes
T8b949e# almost always differ across two independent builds (each entry's
T8b949e# own PEP 552 header + the zip's own per-entry timestamps), so only
T8b949e# trust a blind copy once we've confirmed both slices bundled the
T8b949e# same set of modules. A differing member list would mean the two
T8b949e# Python environments actually resolved different dependencies.
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Tff7b72$(basename Ta5d6ff"Te6edf3$relTa5d6ff"Tff7b72)Ta5d6ff" Tff7b72=Tff7b72= Ta5d6ff"library.zip" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
T8b949e# Exclude *.dist-info provenance files that record *how* a package
T8b949e# was installed rather than anything about the package itself:
T8b949e# direct_url.json/INSTALLER only appear when pip/uv installs from
T8b949e# a local wheel path (true for our manually-built pycodec2 wheel),
T8b949e# and uv_build.json/uv_cache.json only appear when uv has to build
T8b949e# a package from sdist (true for cbor2/cryptography, which don't
T8b949e# publish a macOS x86_64 wheel for this Python version). None of
T8b949e# these are ever read by the running app, only by pip/uv itself.
Te6edf3_dist_info_noiseTff7b72=Ta5d6ff'\.dist-info/(direct_url\.json|INSTALLER|uv_build\.json|uv_cache\.json)$'
Te6edf3arm64_membersTff7b72=Ta5d6ff"Tff7b72$(mktempTff7b72)Ta5d6ff"
Te6edf3x64_membersTff7b72=Ta5d6ff"Tff7b72$(mktempTff7b72)Ta5d6ff"
zipinfo -1 Ta5d6ff"Te6edf3$arm64_fileTa5d6ff" T79c0ff2>/dev/null Tb4b4b4| grep -vE Ta5d6ff"Te6edf3$_dist_info_noiseTa5d6ff" Tb4b4b4| sort >Ta5d6ff"Te6edf3$arm64_membersTa5d6ff"
zipinfo -1 Ta5d6ff"Te6edf3$x64_fileTa5d6ff" T79c0ff2>/dev/null Tb4b4b4| grep -vE Ta5d6ff"Te6edf3$_dist_info_noiseTa5d6ff" Tb4b4b4| sort >Ta5d6ff"Te6edf3$x64_membersTa5d6ff"
Tff7b72if cmp -s Ta5d6ff"Te6edf3$arm64_membersTa5d6ff" Ta5d6ff"Te6edf3$x64_membersTa5d6ff"Tb4b4b4; Tff7b72then
cp Ta5d6ff"Te6edf3$arm64_fileTa5d6ff" Ta5d6ff"Te6edf3$x64_fileTa5d6ff"
Tffa657echo Ta5d6ff"Ta5d6ff unified (library.zip, same bundled module set): Te6edf3$relTa5d6ff"
Te6edf3unifiedTff7b72=Tff7b72$((Te6edf3unified Tff7b72+ T79c0ff1Tff7b72))
rm -f Ta5d6ff"Te6edf3$arm64_membersTa5d6ff" Ta5d6ff"Te6edf3$x64_membersTa5d6ff"
Tff7b72continue
Tff7b72fi
Tffa657echo Ta5d6ff"Ta5d6ffunify-backend: ERROR: Te6edf3$relTa5d6ff bundles a different set of modules on arm64 vs x64Ta5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" cx_Freeze's module finder resolved different transitive imports for" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" .venv and .venv-x64. Check that both install the same locked" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" dependency set (uv.lock) via scripts/ci/github-install-deps.sh and" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" scripts/ci/github-install-macos-x64-python-deps.sh." >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" Only present on arm64 (missing from x64):" >Tb4b4b4&T79c0ff2
comm -23 Ta5d6ff"Te6edf3$arm64_membersTa5d6ff" Ta5d6ff"Te6edf3$x64_membersTa5d6ff" Tb4b4b4| sed Ta5d6ff's/^/ /' >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" Only present on x64 (missing from arm64):" >Tb4b4b4&T79c0ff2
comm -13 Ta5d6ff"Te6edf3$arm64_membersTa5d6ff" Ta5d6ff"Te6edf3$x64_membersTa5d6ff" Tb4b4b4| sed Ta5d6ff's/^/ /' >Tb4b4b4&T79c0ff2
rm -f Ta5d6ff"Te6edf3$arm64_membersTa5d6ff" Ta5d6ff"Te6edf3$x64_membersTa5d6ff"
Tffa657exit T79c0ff1
Tff7b72fi
Te6edf3pkg_dirTff7b72=Ta5d6ff"Tff7b72$(dirname Ta5d6ff"Te6edf3$relTa5d6ff"Tff7b72)Ta5d6ff"
Tff7b72if find Ta5d6ff"Te6edf3$ARM64_DIRTa5d6ff/Te6edf3$pkg_dirTa5d6ff" Ta5d6ff"Te6edf3$X64_DIRTa5d6ff/Te6edf3$pkg_dirTa5d6ff" -maxdepth T79c0ff1 Tffea00\
Tffea00\( -name Ta5d6ff'*.so' -o -name Ta5d6ff'*.dylib' -o -name Ta5d6ff'*.bundle' Tffea00\) -print -quit T79c0ff2>/dev/null Tb4b4b4| grep -q .Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffunify-backend: ERROR: byte mismatch in a native Python package: Te6edf3$relTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff"Ta5d6ff arm64 and x64 cx_Freeze trees differ under Tffd700${Te6edf3pkg_dirTffd700}Ta5d6ff/, which alsoTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" contains native extensions. Copying one arch's .py over the other's" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" .so breaks imports (e.g. NumPy _add_newdoc_ufunc). Rebuild the x64" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" slice with scripts/ci/github-install-macos-x64-python-deps.sh." >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi
cp Ta5d6ff"Te6edf3$arm64_fileTa5d6ff" Ta5d6ff"Te6edf3$x64_fileTa5d6ff"
Tffa657echo Ta5d6ff"Ta5d6ff unified: Te6edf3$relTa5d6ff"
Te6edf3unifiedTff7b72=Tff7b72$((Te6edf3unified Tff7b72+ T79c0ff1Tff7b72))
Tff7b72continue
Tff7b72fi
Te6edf3x64_filetypeTff7b72=Tff7b72$(file --brief --no-pad Ta5d6ff"Te6edf3$x64_fileTa5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657trueTff7b72)
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$x64_filetypeTa5d6ff" Tff7b72=Tff7b72= Mach-O* Tff7b72]Tff7b72] Tff7b72&&
Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$x64_filetypeTa5d6ff" Tff7b72=Tff7b72= *arm64* Tff7b72]Tff7b72] Tff7b72&&
Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$x64_filetypeTa5d6ff" !Tff7b72= *universal* Tff7b72]Tff7b72] Tff7b72&&
Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$x64_filetypeTa5d6ff" !Tff7b72= *x86_64* Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
cp Ta5d6ff"Te6edf3$arm64_fileTa5d6ff" Ta5d6ff"Te6edf3$x64_fileTa5d6ff"
Tffa657echo Ta5d6ff"Ta5d6ffunify-backend: normalized same-arch Mach-O (arm64 in both trees): Te6edf3$relTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" x64 copy replaced with arm64 tree's bytes; @electron/universal" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" will use x64ArchFiles to skip lipo. For a true universal2 binary," >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff" ensure the x64 Python env compiles with CC=\"clang -arch x86_64\"." >Tb4b4b4&T79c0ff2
Te6edf3normalizedTff7b72=Tff7b72$((Te6edf3normalized Tff7b72+ T79c0ff1Tff7b72))
Tff7b72fi
Tff7b72done < <Tff7b72(Tffa657cd Ta5d6ff"Te6edf3$ARM64_DIRTa5d6ff" Tff7b72&& find . -type f -print0Tff7b72)
Te6edf3totalTff7b72=Tff7b72$((Te6edf3unified Tff7b72+ Te6edf3synced Tff7b72+ Te6edf3normalizedTff7b72))
Tff7b72if Tff7b72[Tff7b72[ Te6edf3$dropped -gt T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffunify-backend: WARNING: dropped Te6edf3$droppedTa5d6ff arch-only Mach-O binary/binaries for consistency (pure Python fallback active)Ta5d6ff"
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ Te6edf3$normalized -gt T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffunify-backend: WARNING: normalized Te6edf3$normalizedTa5d6ff same-arch Mach-O file(s) (both trees had arm64; x64ArchFiles will skip lipo)Ta5d6ff"
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ Te6edf3$total -gt T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffunify-backend: synced Te6edf3$syncedTa5d6ff missing file(s), unified Te6edf3$unifiedTa5d6ff differing file(s), normalized Te6edf3$normalizedTa5d6ff same-arch file(s)Ta5d6ff"
Tff7b72else
Tffa657echo Ta5d6ff"unify-backend: all files already identical and present in both trees"
Tff7b72fi
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────